#include <fcntl.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>


#include "sockutil.h"


int tcp_connect(int sock, const char* host, unsigned short port, int timeout)
{
	if( sock < 0 || host == NULL || port==0 || timeout < 0)
		return SOME_ERROR;

	/* set nonblock */
	int flags = fcntl(sock, F_GETFL, 0);
	fcntl(sock, F_SETFL, flags|O_NONBLOCK);

	int error = 0;

	struct sockaddr_in  servaddr;
	memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(port);
    servaddr.sin_addr.s_addr = inet_addr(host);
       
    int ret = connect(sock, (const struct sockaddr *)&servaddr, sizeof(servaddr));
	if( ret < 0 && errno != EINPROGRESS)	
			return -1;

	if( ret == 0)
		goto done;

	fd_set rset, wset;
	FD_ZERO(&rset);
	FD_SET(sock, &rset);
	wset = rset;
	
	struct timeval tv;
	tv.tv_sec = timeout/1000;
	tv.tv_usec = timeout%1000;
	
	ret = select(sock+1, &rset, &wset, NULL, &tv);
	if( ret <= 0 )
	{
		close(sock);
		errno = ETIMEDOUT;
		return -1;
	}
	socklen_t len;
	if( FD_ISSET(sock, &rset) || FD_ISSET(sock, &wset))	
	{
		len = sizeof(error);
		if( getsockopt(sock, SOL_SOCKET, SO_ERROR, &error, &len) < 0)
			return -1;
	}
	else
	{
		//printf("select error: sockfd not set");	
	}

done:
	/* restore file status flags */
	fcntl(sock, F_SETFL, flags);
	if( error)
	{
		close(sock);
		errno = error;
		return SOME_ERROR;
	}
	return 0;
}


int safe_send(int sock, const char* buf, int len, int timeout)
{
	if( sock < 0 || buf == NULL || len < 0 || timeout < 0 )
		return SOME_ERROR;
	int send_count = 0;
	const char* pbuf = buf;
	while (len > 0)
	{
		int rt = send_o(sock, pbuf, len, timeout);
		if (rt < 0){
			break;
		}
		else if (rt == 0){
			break;
		}
		send_count += rt;
		len -= rt;
		pbuf += rt;
	}
	return send_count;
}


int safe_recv(int sock, char* buf, int len, int timeout)
{
	if( sock < 0 || buf == NULL || len < 0 || timeout < 0 )
		return SOME_ERROR;

	int recv_count = 0;
	char* pbuf = buf;
	while (len > 0)
	{
		int rt = recv_o(sock, pbuf, len, timeout);
		if (rt < 0){
			break;
		}
		else if (rt == 0){
			break;
		}
		recv_count += rt;
		len -= rt;
		pbuf += rt;
	}
	return recv_count;

}

int recv_o(int sock, char* buf, int len, int timeout)
{
	if( sock < 0 || buf == NULL || len < 0 || timeout < 0 )
		return SOME_ERROR;

	fd_set	rset;
	struct	timeval tv;
	
	FD_ZERO(&rset);
	FD_SET(sock, &rset);

	tv.tv_sec = timeout/1000;
	tv.tv_usec = timeout%1000;
	if( select(sock+1, &rset, NULL, NULL, &tv) <= 0)
		return SOME_ERROR;
	
	return recv(sock, buf, len, 0);
	
}

int send_o(int sock, const char* buf, int len, int timeout)
{
	if( sock < 0 || buf == NULL || len < 0 || timeout < 0 )
		return SOME_ERROR;

	fd_set	wset;
	struct	timeval tv;
	
	FD_ZERO(&wset);
	FD_SET(sock, &wset);

	tv.tv_sec = timeout/1000;
	tv.tv_usec = timeout%1000;
	
	if( select(sock+1, NULL, &wset, NULL, &tv) <= 0 )	
		return SOME_ERROR;
	
	return send(sock, buf, len, 0);
}

int send_int(int sock, int value, int timeout)
{
	unsigned int ll = htonl((unsigned int)value);	
	char* buf = (char *)malloc(sizeof(int));
	if( buf == NULL)			
		return -1;
	memcpy(buf, &ll, sizeof(ll));
	int ret =  safe_send(sock, buf, sizeof(int), timeout);
	if(buf)	
	{
		free(buf);
		buf = NULL;
	}
	return ret;
	
}

int recv_int(int sock, int timeout)
{
	char* buf = (char *)malloc(sizeof(int)+1);
	int ret = safe_recv(sock, buf, sizeof(int), timeout);
	int value = -1;
	if( ret == sizeof(int))	
	{
		memcpy(&value, buf, sizeof(value));
		value = ntohl(value);
	}
	if(buf)
	{
		free(buf);
		buf = NULL;
	}
	return value;
}
